﻿using Aliyun.Acs.Core;
using Aliyun.Acs.Core.Profile;
using Aliyun.OSS;
using Aliyun.OSS.Common;
using Common.Cache;
using Common.Json;
using Microsoft.Extensions.Configuration;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;

namespace Common.Aliyun
{
    /// <summary>
    /// OSS访问授权（url签名模式，默认授权5分钟）
    /// </summary>
    public class OSSAccessAuthorize
    {
        private readonly IConfiguration configuration;
        private readonly ConfigHelper configHelper;
        /// <summary>
        /// OSS访问授权（url签名模式，默认授权5分钟）
        /// </summary>
        /// <param name="_configuration"></param>
        public OSSAccessAuthorize(IConfiguration _configuration)
        {
            this.configuration = _configuration;
            this.configHelper = new ConfigHelper(this.configuration);
        }
        /// <summary>
        /// 授权访问
        /// </summary>
        /// <param name="url">oss对象访问地址</param>
        /// <param name="expiry">授权过期时间</param>
        /// <returns></returns>
        public string GrantAccess(string url, TimeSpan? expiry = null)
        {
            return GrantAccess(url, expiry, null);
        }
        /// <summary>
        /// OSS链接授权范围
        /// </summary>
        /// <param name="url"></param>
        /// <param name="expiry"></param>
        /// <param name="downloadArg"></param>
        /// <returns></returns>
        public string GrantAccess(string url, TimeSpan? expiry, string downloadArg)
        {
            var objectUri = new Uri(url);
            string ossDomainConfig = this.configuration["OSSDomainMapping"];
            List<OSSDomainConfig> domains = JsonConvert.DeserializeObject<List<OSSDomainConfig>>(ossDomainConfig);
            var matchDomain = domains.Find(x => objectUri.Host.Contains(x.Domain));
            if (matchDomain == null)
            {
                //通过重新构建链接，删除多余的分隔符。旧版文件在域名和路径之前存在多余的分隔符
                return string.Concat(objectUri.Scheme, "://", objectUri.Host, "/", objectUri.PathAndQuery.TrimStart('/'));
            }
            var process = GetOssProcessArg(objectUri);
            string objectName = objectUri.AbsolutePath.TrimStart('/');
            string bucket = string.IsNullOrEmpty(matchDomain.Bucket) ? objectUri.Host.Substring(0, objectUri.Host.IndexOf('.')) : matchDomain.Bucket;

            return GrantAccess(objectName, bucket, process, expiry, downloadArg);
        }
        /// <summary>
        /// OSS链接授权访问
        /// </summary>
        /// <param name="objectName">指定OSS对象名称</param>
        /// <param name="bucket">指定文件bucket名称</param>
        /// <param name="process">指定图片或文件处理参数</param>
        /// <param name="expiry">过期时间</param>
        /// <param name="downloadArg">指定下载参数</param>
        /// <returns></returns>
        public string GrantAccess(string objectName, string bucket, string process, TimeSpan? expiry, string downloadArg)
        {
            var ts = expiry ?? TimeSpan.FromMinutes(5);
            var credential = STSCredential.GetSTSCredentail(bucket, this.configuration);
            if (credential == null)
            {
                return null;
            }
            var endpoint = this.configHelper.GetBucketEndPoint(bucket);
            bool cname = false;
            string bucketEndpoint = endpoint.EndPoint;
            if (!string.IsNullOrEmpty(endpoint.CName))
            {
                cname = true;
                bucketEndpoint = endpoint.CName;
            }
            var clientConfiguration = new ClientConfiguration();
            clientConfiguration.IsCname = cname;
            clientConfiguration.Protocol = Protocol.Https;
            // 创建OSSClient实例。
            var client = new OssClient(bucketEndpoint, credential.AccessKeyId, credential.AccessKeySecret, credential.SecurityToken, clientConfiguration);
            try
            {
                //var metadata = client.GetObjectMetadata(bucket, objectName);
                //var etag = metadata.ETag;
                // 生成签名URL。
                var req = new GeneratePresignedUriRequest(bucket, objectName, SignHttpMethod.Get);
                req.Expiration = DateTime.Now.Add(ts);
                if (!string.IsNullOrEmpty(process))
                {
                    req.Process = process;
                }
                if (!string.IsNullOrEmpty(downloadArg))
                {
                    req.ResponseHeaders.ContentDisposition = downloadArg;
                }
                var uri = client.GeneratePresignedUri(req);
                // 使用签名URL下载文件。
                //return uri.ToString();
                return uri.Scheme == "https" ? uri.ToString() : $"https://{uri.Host}{uri.PathAndQuery}";
            }
            catch (OssException ex)
            {
                DiagnosticHelper.Write("OSSAccessAuthorize.GrantAccess", $"failed with error code: {ex.ErrorCode}", ex);
                return null;
            }
            catch (Exception ex)
            {
                DiagnosticHelper.Write("OSSAccessAuthorize.GrantAccess", $"unexpected exception occurs, {ex.Message}", ex);
                return null;
            }
        }
        /// <summary>
        /// 授权访问
        /// </summary>
        /// <param name="objectName">对象名称</param>
        /// <param name="bucket">OSS bucket名称</param>
        /// <param name="process">图片处理参数</param>
        /// <param name="expiry">过期时间</param>
        /// <returns></returns>
        public string GrantAccess(string objectName, string bucket, string process, TimeSpan? expiry = null)
        {
            return GrantAccess(objectName, bucket, process, expiry, null);
        }


        private void GetOSSObjectInfo(Uri objectUri, out string objectName, out string bucket)
        {
            objectName = objectUri.AbsolutePath.TrimStart('/');
            bucket = objectUri.Host.Substring(0, objectUri.Host.IndexOf('.'));
        }

        /// <summary>
        /// 获取OSS图片处理参数，已吞噬异常
        /// </summary>
        /// <param name="objectUri"></param>
        /// <returns></returns>
        private static string GetOssProcessArg(Uri objectUri)
        {
            try
            {
                string process = null;
                string[] kvs = objectUri.Query?.TrimStart('?')?.Split('&');
                if (kvs != null && kvs.Any())
                {
                    foreach (var item in kvs)
                    {
                        string[] kv = item.Split('=');
                        if (kv[0] == "x-oss-process")
                        {
                            process = kv[1];
                            break;
                        }
                    }
                }
                return process;
            }
            catch
            {
                return null;
            }
        }
    }
    /// <summary>
    /// OSS域名配置
    /// </summary>
    public class OSSDomainConfig
    {
        /// <summary>
        /// OSS域名
        /// </summary>
        public string Domain { get; set; }
        /// <summary>
        /// 对应的bucket名称
        /// </summary>
        public string Bucket { get; set; }

    }
    /// <summary>
    /// bucket信息
    /// </summary>
    public class BucketEndPoint
    {
        /// <summary>
        /// OSS bucket名称
        /// </summary>
        public string Bucket { get; set; }
        /// <summary>
        /// 内网终结点
        /// </summary>
        public string InternalEndpoint { get; set; }
        /// <summary>
        /// 外网终结点
        /// </summary>
        public string EndPoint { get; set; }
        /// <summary>
        /// 自定义域名
        /// </summary>
        public string CName { get; set; }
    }
}
